﻿#include "../control.h"
#include "../ctlStatic.h"
#include "../ctlHelp.h"
#include "../ctlFontQueue.h"
#include <map>
#include <cctype>
#include <Windows.h>

namespace swui
{
	static std::map<HWND, _ctlInterior::majorInfo*> instanceList;

	DWORD CALLBACK redrawSender(LPVOID obj)
	{
		_ctlInterior::majorInfo* instance = (_ctlInterior::majorInfo*)obj;
		while(WaitForSingleObject(instance->hExitEvent, 0) == WAIT_TIMEOUT)
		{
			// 如果 NoDrawingEvent 有信号（表示当前没有在绘制）
			if(WaitForSingleObject(instance->hNoDrawingEvent, 0) != WAIT_TIMEOUT	
				&& instance->isRedrawRequired)		// 需要重画
				SetEvent(instance->hRedrawEvent);
			Sleep(1000 / instance->redrawFrequency);
		}
		return 0;
	}
	DWORD CALLBACK redrawReceive(LPVOID obj)
	{
		_ctlInterior::majorInfo* instance = (_ctlInterior::majorInfo*)obj;
		HANDLE events[2] = { instance->hRedrawEvent, instance->hExitEvent };
		while(WaitForMultipleObjects(2, events, 0, INFINITE) == WAIT_OBJECT_0)
		{
			// 将 NoDrawingEvent 设置成无信号（表示正在绘制）
			ResetEvent(instance->hNoDrawingEvent);
			PostMessage(instance->hWnd, WM_PAINT, 0, 0);
			HANDLE event3s[2] = { instance->hNoDrawingEvent, instance->hExitEvent };
			WaitForMultipleObjects(2, event3s, 0, INFINITE);		//等待绘制完成或者退出
			ResetEvent(instance->hRedrawEvent);
		}
		return 0;
	}
	LRESULT CALLBACK ctlRoot::_ctlCallBack(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		_ctlInterior::majorInfo* instance = instanceList[hWnd];
		if(!instance) return DefWindowProc(hWnd, uMsg, wParam, lParam);
		if(uMsg == WM_PAINT)
		{
			//开始计时
			clock_t timeBegin = clock();

			HDC hDC = GetDC(hWnd);
			RECT rect;
			GetClientRect(hWnd, &rect);
			_ctlInterior::drawInfo info;
				
			//清除需要
			instance->isRedrawRequired = false;
			_ctlInterior::compatibleDC* tdc = instance->fixedDC.tdc;
			RECT regionBox;
//			unsigned drewCount = 0;
			do {
				//设置裁剪区域
				if(instance->notallowedDrawCount)
				{
					SelectClipRgn(tdc->hdc, NULL);
					DeleteObject(instance->redrawRegion);
					RECT rcv;
					GetClientRect(hWnd, &rcv);
					instance->redrawRegion = CreateRectRgnIndirect(&rcv);
				} else {
					SelectClipRgn(tdc->hdc, instance->redrawRegion);
				}
				GetClipBox(tdc->hdc, &regionBox);
				info.drawRegion = instance->redrawRegion;
				SetViewportOrgEx(tdc->hdc, 0, 0, NULL);
				//绘制背景
				instance->notallowedDrawCount = 0;
				if(instance->isUnicode)
					CallWindowProcW(instance->wndProc, hWnd, WM_ERASEBKGND, (WPARAM)tdc->hdc, 0);
				else CallWindowProcA(instance->wndProc, hWnd, WM_ERASEBKGND, (WPARAM)tdc->hdc, 0);
				//绘制Root及子控件
				if(instance->root->isShow())
				{
					instance->notallowedDrawCount = 0;
					info.hDC = tdc->hdc;
					info.graphics = instance->fixedDC.graphics;
					instance->root->_alpha_redraw_(&info);
					if(instance->root->_combine_child_rgn(info.drawRegion))
						SelectClipRgn(info.hDC, info.drawRegion);
					instance->root->_eventTrans.draw(&info);
				}
				//绘制前景
				if(instance->isUnicode)
					CallWindowProcW(instance->wndProc, hWnd, WM_PAINT, (WPARAM)tdc->hdc, 0);
				else CallWindowProcA(instance->wndProc, hWnd, WM_PAINT, (WPARAM)tdc->hdc, 0);
			} while(instance->notallowedDrawCount /*&& ++drewCount < 3*/);
			BitBlt(hDC, regionBox.left, regionBox.top, 
				regionBox.right - regionBox.left, regionBox.bottom - regionBox.top,
				tdc->hdc, regionBox.left, regionBox.top, SRCCOPY);
			ReleaseDC(hWnd, hDC);
			if(instance->isUnicode) DefWindowProcW(hWnd, uMsg, wParam, lParam);
			else DefWindowProcA(hWnd, uMsg, wParam, lParam);
			
			//计时结束
			instance->preDrawTime = clock() - timeBegin;
			//重绘完成
			SetEvent(instance->hNoDrawingEvent);	
			SetRectRgn(instance->redrawRegion, 0, 0, 0, 0);
			
			return 1;
		} else if(uMsg == WM_SIZE) {
			ctlRoot* root = instance->root;
			root->lockDrawing();
			root->_size_change();
			root->_move_change();
			if(instance->fixedDC.tdc->width < root->getRight()
				|| instance->fixedDC.tdc->height < root->getBottom())
			{
				delete instance->fixedDC.tdc;
				instance->fixedDC.tdc = new _ctlInterior::compatibleDC(
					root->getRight() + 10, root->getBottom() + 10);
				delete instance->fixedDC.graphics;
				HDC hdc = instance->fixedDC.tdc->GetDC();
				instance->fixedDC.graphics = new Gdiplus::Graphics(hdc);
				SetBkMode(hdc, TRANSPARENT);
				instance->fixedDC.graphics->SetSmoothingMode(
					Gdiplus::SmoothingModeHighQuality);
			}
			root->unlockDrawing();
			root->redrawAllControl();
			SetEvent(instance->hRedrawEvent);
			SendMessageW(hWnd, WM_PAINT, 0, 0);
		} else if(uMsg == WM_ERASEBKGND) {
			instance->root->redrawAllControl();
			return TRUE;
		} else if(uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN
			|| uMsg == WM_LBUTTONUP || uMsg == WM_RBUTTONUP
			|| uMsg == WM_LBUTTONDBLCLK || uMsg == WM_RBUTTONDBLCLK) {
			ctlMouseEvent me;
			me.wheelDelta = 0;
			me.eventModel = ctlMouseEventModel::cut;
			switch(uMsg)
			{
			case WM_LBUTTONDOWN: 
				me.eventType = ctlMouseEventType::leftDown; break;
			case WM_RBUTTONDOWN:
				me.eventType = ctlMouseEventType::rightDown; break;
			case WM_LBUTTONUP: 
				me.eventType = ctlMouseEventType::leftUp; break;
			case WM_RBUTTONUP:
				me.eventType = ctlMouseEventType::rightUp; break;
			case WM_LBUTTONDBLCLK:
				me.eventType = ctlMouseEventType::leftDBClick; break;
			case WM_RBUTTONDBLCLK:
				me.eventType = ctlMouseEventType::rightDBClick; break;
			}
			me.absolutePos = me.mousePos = ctlPoint(MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y);
			if(instance->mouseCapture)
			{	//坐标转换
				ctlRoot* pre = instance->mouseCapture;
				while(pre)
				{
					me.mousePos.x -= pre->getLeft();
					me.mousePos.y -= pre->getTop();
					pre = pre->getParent();
				}
				instance->mouseCapture->_eventTrans.vicMouse(me, instance->mouseCapture, false);
			}
			else instance->root->_eventTrans.mouse(&me);
		} else if(uMsg == WM_MOUSEMOVE) {
			ctlMouseEvent me;
			me.wheelDelta = 0;
			me.eventModel = ctlMouseEventModel::cut;
			me.eventType = ctlMouseEventType::move;
			me.absolutePos = me.mousePos = ctlPoint(MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y);
			me.mousePos.x -= instance->root->getLeft();
			me.mousePos.y -= instance->root->getTop();
			if(instance->moveCapture == nullptr)
				instance->root->_eventTrans.mouse(&me);
			else {
				me.mousePos = instance->moveCapture->_eventTrans.pointTrans(me.mousePos, instance->root);
				me.eventFrom = instance->moveCapture->getParent();
				instance->moveCapture->_mouse_event_(&me);
			}
		} else if(instance->focus && (uMsg == WM_KEYDOWN || uMsg == WM_KEYUP)) {
			ctlKeyEvent ke;
			switch(uMsg)
			{
			case WM_KEYDOWN:
				ke.eventType = ctlKeyEventType::down; break;
			case WM_KEYUP:
				ke.eventType = ctlKeyEventType::up; break;
			}
			ke.charInfo.virtualKey = (int)wParam;
			instance->focus->_key_event_(&ke);
		} else if(instance->focus && uMsg == WM_CHAR) {
			ctlKeyEvent ke;
			ke.eventType = ctlKeyEventType::input;
			if(instance->isUnicode)
			{
				ke.charInfo.wtext = (std::wstring::value_type)wParam;
				instance->focus->_key_event_(&ke);
			} else {
				if(instance->isDoubleChar)
				{
					instance->isDoubleChar = false;
					ke.charInfo.text[0] = instance->tempchar;
					ke.charInfo.text[1] = (char)wParam;
					ke.charInfo.text[2] = '\0';
					instance->focus->_key_event_(&ke);
				} else if((char)wParam < 0) {
					instance->isDoubleChar = true;
					instance->tempchar = (char)wParam;
				} else {
					ke.charInfo.text[0] = (char)wParam;
					ke.charInfo.text[1] = ke.charInfo.text[2] = '\0';
					instance->focus->_key_event_(&ke);
				}
			}
		} else if(instance->focus && uMsg == WM_MOUSEWHEEL) {
			ctlMouseEvent me;
			me.eventModel = ctlMouseEventModel::cut;
			me.eventType = ctlMouseEventType::wheel;
			me.mousePos = me.absolutePos = ctlPoint(MAKEPOINTS(lParam).x, MAKEPOINTS(lParam).y);
			ctlRoot* rt = instance->focus;
			while(rt)
			{
				me.mousePos.x -= rt->getLeft();
				me.mousePos.y -= rt->getTop();
				rt = rt->getParent();
			}
			me.wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA;
			me.eventFrom = instance->focus->getParent();
			instance->focus->_mouse_event_(&me);
		} else if(uMsg == WM_SETFOCUS) {
			if(instance->preFocus)
				instance->preFocus->setFocus();
		} else if(uMsg == WM_KILLFOCUS) {
			if(instance->focus)
				instance->focus->killFocus();
		} else if(uMsg == WM_SETCURSOR) {
			if(instance->bindCursor && LOWORD(lParam) == HTCLIENT)
			{
				SetCursor(instance->hCursor);
				return TRUE;
			}
		}
		if(instance->isUnicode)
			return CallWindowProcW(instance->wndProc, hWnd, uMsg, wParam, lParam);
		else return CallWindowProcA(instance->wndProc, hWnd, uMsg, wParam, lParam);
	}

	ctlRoot* ctlRoot::init(ctlInitInfo* info)
	{
		if(info == 0) return 0;
		if(instanceList.count(info->hWnd)) return 0;
		_ctlInterior::majorInfo* instance = new _ctlInterior::majorInfo;
		ctlStatic* root = new ctlStatic;
		//设置事件对象
		instance->hRedrawEvent = CreateEvent(0, 1, 0, 0);
		instance->hExitEvent = CreateEvent(0, 1, 0, 0);
		instance->hNoDrawingEvent = CreateEvent(0, 1, 1, 0);
		//设置基本信息
		instance->gdiplusInstance = new _ctlInterior::majorInfo::_GraphicsInit;
		instance->isRedrawRequired = false;
		instance->drawingCount = 0;
		instance->preDrawTime = 0;
		instance->hWnd = info->hWnd;
		instance->mouseCapture = nullptr;
		instance->moveCapture = nullptr;
		instance->notallowedDrawCount = 0;
		instance->root = root;
		instance->focus = root;
		instance->preFocus = nullptr;
		instance->redrawFrequency = 60;
		instance->mouseStay = nullptr;
		instance->isDoubleChar = false;
		instance->bindCursor = false;
		instance->isUnicode = info->isUnicode;
		instance->constantFont = std::make_shared<ctlFontQueue>();
		instance->constantImage = std::make_shared<ctlImageQueue>();
		instance->redrawRegion = CreateRectRgn(0, 0, 0, 0);
		//接管消息处理事件
		instance->wndProc = (WNDPROC)GetWindowLong(info->hWnd, GWL_WNDPROC);
		if(info->isUnicode)
			SetWindowLongW(info->hWnd, GWL_WNDPROC, (LONG)ctlRoot::_ctlCallBack);
		else SetWindowLongA(info->hWnd, GWL_WNDPROC, (LONG)ctlRoot::_ctlCallBack);
		instanceList[info->hWnd] = instance;
		RECT rect;
		//设置root控件信息
		GetClientRect(info->hWnd, &rect);
		root->_isRoot = true;
		root->_eventTrans.setMajorInfoAndRoot(instance, root);
		root->create(0, ctlRect(0, 0, rect.right, rect.bottom));
		root->setSizeFormat(sizeFormat::percent, ctlDirection::xy);
		root->sizePercent(1.0f, 1.0f);
		info->root = root;
		//创建双缓冲 DC
		instance->fixedDC.tdc = new _ctlInterior::compatibleDC(
			root->getRight() + 10, root->getBottom() + 10);
		HDC hdc = instance->fixedDC.tdc->GetDC();
		instance->fixedDC.graphics = new Gdiplus::Graphics(hdc);
		instance->fixedDC.graphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
		SetBkMode(hdc, TRANSPARENT);
		//创建线程
		instance->hRedrawSenderThread = CreateThread(0, 0, &redrawSender, (LPVOID)instance, 0, 0);
		instance->hRedrawReceiveThread = CreateThread(0, 0, &redrawReceive, (LPVOID)instance, 0, 0);
		//设置插入符信息
		instance->hCaret.bindExit(instance->hExitEvent, info->hWnd);
		return root;
	}
	bool ctlRoot::uninit(ctlInitInfo* info)
	{
		if(info == 0) return false;
		if(!instanceList.count(info->hWnd)) return false;
		_ctlInterior::majorInfo *instance = instanceList[info->hWnd];
		instanceList.erase(info->hWnd);
		bool isUnicode = instance->isUnicode;
		instance->hCaret.enable(false);
		DeleteObject(instance->redrawRegion);
		delete instance->root;
		delete instance->fixedDC.tdc;
		delete instance->fixedDC.graphics;
		//关闭线程，清理事件对象
		SetEvent(instance->hExitEvent);
		HANDLE threads[2] = { instance->hRedrawReceiveThread, 
			instance->hRedrawSenderThread };
		WaitForMultipleObjects(2, threads, 1, INFINITE);
		for(HANDLE object : threads)
			CloseHandle(object);
		instance->hCaret.waitExit();
		CloseHandle(instance->hExitEvent);
		CloseHandle(instance->hRedrawEvent);
		CloseHandle(instance->hNoDrawingEvent);
		//重置消息处理事件
		if(isUnicode) SetWindowLongW(instance->hWnd, GWL_WNDPROC, (LONG)instance->wndProc);
		else SetWindowLongA(instance->hWnd, GWL_WNDPROC, (LONG)instance->wndProc);
		//延迟删除 GDI+ 实例
		auto gdiplusInstance = instance->gdiplusInstance;
		delete instance;
		delete gdiplusInstance;
		InvalidateRect(info->hWnd, 0, TRUE);
//		SendMessage(info->hWnd, WM_PAINT, 0, 0);
		return true;
	}
	unsigned ctlRoot::getRedrawFrequency()
	{
		return _eventTrans.getMajorInfo()->redrawFrequency;
	}
	void ctlRoot::setRedrawFrequency(unsigned frequency)
	{
		_eventTrans.getMajorInfo()->redrawFrequency = frequency;
	}

	void ctlRoot::lockDrawing(bool isAll) {
		if(isCreated()) 
		{
			if(isAll) ++_eventTrans.getMajorInfo()->drawingCount;
			else ++_redrawLockCount;
		}
	}
	void ctlRoot::unlockDrawing(bool isAll) {
		if(isCreated())
		{
			if(isAll)
			{
				if(_eventTrans.getMajorInfo()->drawingCount != 0) 
					--_eventTrans.getMajorInfo()->drawingCount;
			} else {
				if(_redrawLockCount != 0)
					--_redrawLockCount;
			}
		}
	}
	unsigned ctlRoot::getDrawingLockCount(bool isAll) {
		unsigned result = 0;
		if(isCreated())
		{
			if(isAll) result = _eventTrans.getMajorInfo()->drawingCount;
			else result = _redrawLockCount;
		}
		return result;
	}
	std::clock_t ctlRoot::getPreDrawTimeCost()
	{
		return _eventTrans.getMajorInfo()->preDrawTime;
	}
	void ctlRoot::_capture_mouse_move()
	{
		_eventTrans.getMajorInfo()->moveCapture = this;
	}
	void ctlRoot::_release_mouse_move()
	{
		_eventTrans.getMajorInfo()->moveCapture = nullptr;
	}
	void ctlRoot::redrawAllControl()
	{
		if(!isCreated() || !isVisible()) return;
		RECT rect;
		auto instance = _eventTrans.getMajorInfo();
		GetClientRect(instance->hWnd, &rect);
		ctlRect requireRect(0, 0, rect.right, rect.bottom);
		redrawRect(requireRect, true);
	}
	void ctlRoot::redrawRect(ctlRect rc, bool absolute)
	{
		if(!isCreated()) return;
		if(isDrawingLocked(true) || isDrawingLocked(false)) return;
		if(absolute == false)
		{
			ctlRoot* pre = getParent();
			while(pre)
			{
				if(pre->isDrawingLocked(false)) return;
				rc.x += pre->getLeft();
				rc.y += pre->getTop();
				pre = pre->getParent();
			}
		}
		auto instance = _eventTrans.getMajorInfo();
		if(WaitForSingleObject(instance->hNoDrawingEvent, 0) == WAIT_TIMEOUT)
			++_eventTrans.getMajorInfo()->notallowedDrawCount;
		rc.cx += 2, rc.cy += 2;
		// 合并区域
		HRGN tempRegion = CreateRectRgn(rc.x, rc.y, rc.cx + rc.x, rc.cy + rc.y);
		CombineRgn(instance->redrawRegion, instance->redrawRegion, tempRegion, RGN_OR);
		DeleteObject(tempRegion);
		instance->isRedrawRequired = true;
	}

	void* ctlRoot::heapAlloc(size_t size, void(*deleter)(void*))
	{
		char *value = new(std::nothrow)char[size];
		if(value !=  nullptr)
		{
			_heap_alloc_recorder.insert(
				std::pair<char*, void(*)(void*)>(value, deleter));
		}
		return value;
	}
	void ctlRoot::heapFree(void* ptr)
	{
		char* value = reinterpret_cast<char*>(ptr);
		auto iter = _heap_alloc_recorder.find(value);
		if(iter != _heap_alloc_recorder.end())
		{
			auto elem = *iter;
			if(elem.first == value)
			{
				if(elem.second != 0)
					elem.second(ptr);
				delete[] value;
				_heap_alloc_recorder.erase(iter);
			}
		}
	}
}